import random
from datetime import datetime
from decimal import Decimal

from django.db import transaction
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import SerializerMethodField
from rest_framework.serializers import ModelSerializer

from shopping.models import Classification, Commodity, CommodityImg, ShoppingCart, Order, OrderGoods
from users.models import Address


# 分类序列化器

class ClassificationSerializer(ModelSerializer):
    class Meta:
        model = Classification
        fields = ["id", "name"]


class ParentClassificationSerializer(ModelSerializer):
    classification_set = ClassificationSerializer(many=True, read_only=True)

    class Meta:
        model = Classification
        fields = ["id", "name", "classification_set"]


# 商品图片序列化器
class CommodityImgSerializer(ModelSerializer):
    class Meta:
        model = CommodityImg
        exclude = ['is_delete']


# 商品序列化器
class CommoditySerializer(ModelSerializer):
    classification1_name = serializers.CharField(source='classification1.name', read_only=True)
    classification2_name = serializers.CharField(source='classification2.name', read_only=True)
    commodityimg_set = CommodityImgSerializer(many=True, read_only=True)

    class Meta:
        model = Commodity
        exclude = ['is_delete']
        extra_kwargs = {
            # 销量
            "sales": {'read_only': True},
            # 评价数量
            "comments": {'read_only': True},
        }


# 购物车序列化器
class ShoppingCartSerializer(ModelSerializer):
    commodityDetail = SerializerMethodField()

    class Meta:
        model = ShoppingCart
        fields = "__all__"

    def validate(self, attr):
        # 不能小于0   或者 data["commodity"]得到商品 的stock 库存
        if attr["number"] <= 0 or attr["number"] > attr["commodity"].stock:
            raise serializers.ValidationError("商品不能超出库存数 error")
        return attr

    def get_commodityDetail(self, shoppingcart):  # 把当前模型对象传入
        # 商品详情数据  进行序列化返回
        return CommoditySerializer(shoppingcart.commodity).data


# 订单序列化器
class OrderSerializer(ModelSerializer):
    cart = serializers.ListField(write_only=True)  # 得到前端传入过来要结算的购物车商品

    class Meta:
        model = Order
        fields = "__all__"

        read_only_fields = ["order_id", "total_count", "total_amount", "status", "user"]

    def create(self, validated_data):
        # 获取用户信息
        user = self.context['request'].user
        # 订单编号
        order_id = datetime.now().strftime("%Y%m%d%H%M%S") + "%6d%02d" % (user.id, random.randint(1, 99))
        # 获取前端传入的支付方式
        pay_method = validated_data.get("pay_method")
        # 获取前端传入的地址id
        try:
            address = Address.objects.get(id=validated_data.get("address"))
        except Address.DoesNotExist:
            raise ValidationError("Address does not exist")
        # 得到地址id后 凭借成  省市区详细地址【收货人】手机号
        address_s = f'{address.province.name}{address.city.name}{address.district.name}{address.place}【{address.receiver}收】{address.mobile}'
        with transaction.atomic():
            save_point = transaction.savepoint()
            # 创建订单信息
            try:
                order = Order.objects.create(
                    order_id=order_id,
                    pay_method=pay_method,
                    total_count=0,
                    total_amount=Decimal("0.00"),
                    status=1 if pay_method == 1 else 2,
                    user=user,
                    address=address_s,
                )
                # 获取订单商品信息
                carts = validated_data.get("cart")
                # 循环获取每一件商品
                for cart_id in carts:
                    while True:
                        try:
                            # 得到商品要结算的商品数据
                            cart = ShoppingCart.objects.get(id=cart_id)
                            print(cart.commodity.sales)
                        except ShoppingCart.DoesNotExist:
                            raise serializers.ValidationError("购物信息不存在")
                        # 得到购物车对应商品对象
                        commodity = cart.commodity
                        # 得到商品限制的库存和销量
                        origin_stock = commodity.stock
                        origin_sales = commodity.sales
                        # 核算 购买的数量是否超过库存
                        if cart.number > origin_stock:
                            raise serializers.ValidationError("库存不足")
                        # 购买之后库存减少
                        new_stock = origin_stock - cart.number
                        new_sales = origin_sales + cart.number
                        # 更新保存
                        # commodity.stock = new_stock
                        # commodity.sales = new_sales
                        # commodity.save()
                        res = Commodity.objects.filter(id=commodity.id, stock=origin_stock).update(stock=new_stock,
                                                                                                   sales=new_sales)
                        if not res:
                            continue
                        # 创建订单对应的商品数据
                        OrderGoods.objects.create(
                            number=cart.number,
                            price=commodity.price,
                            order=order,
                            commodity=commodity
                        )
                        # 更新订单表数据
                        order.total_count += cart.number
                        # 更新订单金额
                        order.total_amount += (commodity.price * cart.number)
                        break
                order.save()
                # 商品购买完 清除购物车已结算商品
                ShoppingCart.objects.filter(id__in=carts).delete()

            except Exception as e:
                print('error', e)
                transaction.savepoint_rollback(save_point)
                raise serializers.ValidationError(e)
            else:
                transaction.savepoint_commit(save_point)
        return order
